﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Web.Mvc;
using System.Web.UI;

namespace MvcImproved.Generic.Controls
{
	public delegate void MvcControlItemDataBound<T>(Control parentControl, Control boundControl, T data);

	[ParseChildren(true)]
	[PersistChildren(false)]
	public class Repeater<TDataItem, TMemberValue, TMemberValueInstance, TArrayIndexMemberValue> : NamedGenericControl<TDataItem, TMemberValue>
		where TMemberValue : IEnumerable<TMemberValueInstance>
	{
		protected Func<TMemberValueInstance, TArrayIndexMemberValue> IndexValueMemberLambda;

		public string ArrayIndexMember
		{
			get;
			set;
		}

		public event MvcControlItemDataBound<TMemberValueInstance> OnItemDataBound;

		public Repeater(Func<TDataItem, TMemberValue> valueMember, Func<TMemberValueInstance, TArrayIndexMemberValue> indexValueMember)
			: this(valueMember)
		{
			IndexValueMemberLambda = indexValueMember;
		}

		public Repeater(Func<TDataItem, TMemberValue> valueMember)
			: base(valueMember)
		{}

		[DefaultValue(null)]
		[Browsable(false)]
		[PersistenceMode(PersistenceMode.InnerProperty)]
		[GenericTemplateContainer(typeof(RepeaterItem<,>), 2, 3)]
		[TemplateInstance(TemplateInstance.Multiple)]
		public ITemplate ItemTemplate
		{
			get;
			set;
		}

		[DefaultValue(null)]
		[Browsable(false)]
		[PersistenceMode(PersistenceMode.InnerProperty)]
		[GenericTemplateContainer(typeof(RepeaterItem<,>), 2, 3)]
		[TemplateInstance(TemplateInstance.Multiple)]
		public ITemplate AlternatingItemTemplate
		{
			get;
			set;
		}

		[DefaultValue(null)]
		[Browsable(false)]
		[PersistenceMode(PersistenceMode.InnerProperty)]
		[GenericTemplateContainer(typeof(RepeaterItem<,>), 2, 3)]
		[TemplateInstance(TemplateInstance.Single)]
		public ITemplate EmptyDataTemplate
		{
			get;
			set;
		}

		[DefaultValue(null)]
		[Browsable(false)]
		[PersistenceMode(PersistenceMode.InnerProperty)]
		[GenericTemplateContainer(typeof(RepeaterItem<,>), 2, 3)]
		[TemplateInstance(TemplateInstance.Single)]
		public ITemplate LastItemDataTemplate
		{
			get;
			set;
		}

		public string TemplateCssClass { get; set; }
		public string AlternatingTemplateCssClass { get; set; }
		public string LastItemCssClass { get; set; }
		public bool AlternateAppendsToItem { get; set; }
		public int AlternateEvery { get; set; }
		public HtmlTextWriterTag TemplateWrapper { get; set; }


		private void InstantiateControls(IEnumerable<TMemberValueInstance> dataItems)
		{
			// Dummy control to which we parent all the data item controls
			Control containerControl = new Control();

			List<Control> destination = new List<Control>();
			bool hasData = false;
			if (dataItems != null)
			{
				int index = 0;
				int count = 0;

				if (LastItemDataTemplate != null)
				{
					foreach (TMemberValueInstance dataItem in dataItems)
					{
						count++;
					}
				}

				foreach (TMemberValueInstance dataItem in dataItems)
				{
					TArrayIndexMemberValue arrayIndexValue = default(TArrayIndexMemberValue);
					if (IndexValueMemberLambda != null)
						arrayIndexValue = IndexValueMemberLambda(dataItem);

					hasData = true;
					RepeaterItem<TMemberValueInstance, TArrayIndexMemberValue> repeaterItem = new RepeaterItem<TMemberValueInstance, TArrayIndexMemberValue>(index, dataItem, arrayIndexValue, IndexValueMemberLambda == null)
					{
						ViewData = new ViewDataDictionary<TMemberValueInstance>(dataItem)
					};
					HtmlTextWriterTag templateWrapper = TemplateWrapper;

					if ((templateWrapper == HtmlTextWriterTag.Unknown) && (TemplateCssClass != null))
						templateWrapper = HtmlTextWriterTag.Span;

					repeaterItem.Wrapper = templateWrapper;

					bool alternating = (AlternatingItemTemplate != null) && ((index % AlternateEvery) == (AlternateEvery - 1));
					bool lastItem = (LastItemDataTemplate != null) && (index == (count - 1));

					if (!lastItem)
					{
						if ((!alternating) || AlternateAppendsToItem)
						{
							repeaterItem.CssClass = TemplateCssClass;
							ItemTemplate.InstantiateIn(repeaterItem);
						}

						if (alternating)
						{
							repeaterItem.CssClass = AlternatingTemplateCssClass;
							AlternatingItemTemplate.InstantiateIn(repeaterItem);
						}
					}
					else
					{
						repeaterItem.CssClass = LastItemCssClass;
						LastItemDataTemplate.InstantiateIn(repeaterItem);
					}

					if (OnItemDataBound != null)
						OnItemDataBound(this, repeaterItem, dataItem);

					containerControl.Controls.Add(repeaterItem);

					index++;
				}
			}


			if ((!hasData) && (EmptyDataTemplate != null))
			{
				// If there was no data, instantiate the EmptyDataTemplate
				Control emptyDataContainer = new Control();
				EmptyDataTemplate.InstantiateIn(emptyDataContainer);
				containerControl.Controls.Add(emptyDataContainer);
			}

			Controls.Add(containerControl);

			containerControl.DataBind();
		}

		protected override void OnPreRender(EventArgs e)
		{
			base.OnPreRender(e);

			if (!DesignMode)
				InstantiateControls((DataValue != null) ? ValueMemberLambda(DataValue) : (IEnumerable<TMemberValueInstance>)null);
		}

		protected override void Render(HtmlTextWriter writer)
		{
			//If we're in design mode, add some dummy instances to render
			if (DesignMode)
			{
				//Create some dummy instances in a list to render a preview
				List<TMemberValueInstance> dataItems = new List<TMemberValueInstance>();
				for (int i = 0; i < 3; ++i)
				{
					dataItems.Add(default(TMemberValueInstance));
				}

				InstantiateControls(dataItems);
			}

			base.Render(writer);
		}
	}

	// Non-generic version of the control that's used in the actual markup
	[ControlBuilder(typeof(GenericControlBuilder))]
	[GenericControlType(typeof(Repeater<,,,>), MemberValue = "ValueMember", GenericParameters = "ModelType", SubMemberValue = "ArrayIndexMember")]
	public class Repeater : Repeater<object, IEnumerable<object>, object, object>
	{
		public Repeater()
			: base(null, null)
		{ }

		public string ModelType
		{
			get;
			set;
		}
	}
}
